import tkinter as tk
import tkinter as tk
from tkinter import messagebox

def gcd_euclidean(*numbers, verbose=False):
    """
    计算多个数的最大公约数(GCD)
    使用欧几里得算法实现
    :param numbers: 要计算的整数序列
    :param verbose: 是否打印计算过程
    :return: 这些数的最大公约数
    """
    if len(numbers) < 2:
        raise ValueError("至少需要两个数才能计算GCD")
        
    current_gcd = numbers[0]
    for num in numbers[1:]:
        a, b = abs(current_gcd), abs(num)
        steps = []
        if verbose:
            print(f"计算 {a} 和 {b} 的GCD (欧几里得算法)")
            
        while b != 0:
            if verbose:
                steps.append(f"{a} = {b} * {a//b} + {a%b}")
            a, b = b, a % b
            
        if verbose and steps:
            print("计算步骤:")
            for step in steps:
                print(step)
            print(f"最终结果: {a}")
            
        current_gcd = a
        
    return current_gcd

def gcd_binary(*numbers, verbose=False):
    """
    计算多个数的最大公约数(GCD)
    使用二进制GCD算法实现
    :param numbers: 要计算的整数序列
    :param verbose: 是否打印计算过程
    :return: 这些数的最大公约数
    """
    if len(numbers) < 2:
        raise ValueError("至少需要两个数才能计算GCD")
        
    current_gcd = numbers[0]
    for num in numbers[1:]:
        a, b = abs(current_gcd), abs(num)
        shift = 0
        steps = []
        
        if verbose:
            print(f"计算 {a} 和 {b} 的GCD (二进制算法)")
        
        # GCD(0, b) = b; GCD(a, 0) = a
        if a == 0:
            current_gcd = b
            continue
        if b == 0:
            current_gcd = a
            continue
            
        # 计算a和b的公共2的幂
        while ((a | b) & 1) == 0:
            a >>= 1
            b >>= 1
            shift += 1
            if verbose:
                steps.append(f"a和b都是偶数，同时除以2: a={a}, b={b}")
        
        while (a & 1) == 0:
            a >>= 1
            if verbose:
                steps.append(f"a是偶数，除以2: a={a}")
        
        while b != 0:
            while (b & 1) == 0:
                b >>= 1
                if verbose:
                    steps.append(f"b是偶数，除以2: b={b}")
            
            if a > b:
                a, b = b, a
                if verbose:
                    steps.append(f"交换a和b: a={a}, b={b}")
            
            b -= a
            if verbose:
                steps.append(f"b减去a: b={b}")
        
        current_gcd = a << shift
        
        if verbose and steps:
            print("计算步骤:")
            for step in steps:
                print(step)
            print(f"最终结果: {current_gcd}")
        
    return current_gcd

def gcd(*numbers, verbose=False, algorithm="binary"):
    """
    计算多个数的最大公约数(GCD)
    :param numbers: 要计算的整数序列
    :param verbose: 是否打印计算过程
    :param algorithm: 使用的算法 ('binary' 或 'euclidean')
    :return: 这些数的最大公约数
    """
    if algorithm == "binary":
        return gcd_binary(*numbers, verbose=verbose)
    else:
        return gcd_euclidean(*numbers, verbose=verbose)
    if verbose:
        print(f"开始计算 {a} 和 {b} 的最大公约数(GCD)")
        print("欧几里得算法步骤:")
        print(f"初始值: a = {a}, b = {b}")
    
    steps = 0
    while b != 0:
        steps += 1
        remainder = a % b
        if verbose:
            print(f"步骤 {steps}: {a} = {b} * {a//b} + {remainder}")
            print(f"更新值: a = {b}, b = {remainder}")
        a, b = b, remainder
    
    if verbose:
        print(f"计算完成，GCD = {a}")
        print(f"总共进行了 {steps} 次迭代")
    return a

class GCDCalculator:
    def __init__(self, master=None):
        if master is None:
            master = tk.Tk()
        self.master = master
        self.master.title("GCD计算器")
        
        # 输入框
        self.label1 = tk.Label(self.master, text="输入数字(用逗号分隔)或分数(a/b):")
        self.label1.grid(row=0, column=0, padx=5, pady=5)
        self.entry1 = tk.Entry(self.master)
        self.entry1.grid(row=0, column=1, padx=5, pady=5)
        
        # 示例提示
        self.example_label = tk.Label(self.master, text="例如: 24,36,60 或 18/24", fg="gray")
        self.example_label.grid(row=1, column=0, columnspan=2, pady=5)
        
        # 计算按钮
        self.calculate_button = tk.Button(self.master, text="计算GCD", command=self.calculate_gcd)
        self.calculate_button.grid(row=2, column=0, pady=10)
        
        # 分数化简按钮
        self.simplify_button = tk.Button(self.master, text="化简分数", command=self.simplify_fraction)
        self.simplify_button.grid(row=2, column=1, pady=10)
        
        # 数学游戏按钮
        self.game_button = tk.Button(self.master, text="数学游戏", command=self.start_math_game)
        self.game_button.grid(row=3, column=0, pady=10)
        
        # 游戏输入框
        self.game_entry = tk.Entry(self.master)
        self.game_entry.grid(row=3, column=1, pady=10)
        self.game_entry.grid_remove()
        
        # 提交按钮
        self.submit_button = tk.Button(self.master, text="提交答案", command=self.check_game_answer)
        self.submit_button.grid(row=4, column=0, columnspan=2, pady=10)
        self.submit_button.grid_remove()
        
        # 结果显示
        self.result_label = tk.Label(self.master, text="结果:", font=('Arial', 12))
        self.result_label.grid(row=5, column=0, columnspan=2)
        
        # 分数显示
        self.score_label = tk.Label(self.master, text="分数: 0", font=('Arial', 12))
        self.score_label.grid(row=6, column=0, columnspan=2)
        self.score = 0
        
        # 游戏状态
        self.game_active = False
        self.current_numbers = (0, 0)
        self.current_gcd = 0
    

    

    
    def lcm(self, a, b):
        """计算两个数的最小公倍数(LCM)
        公式: LCM(a,b) = |a*b| // GCD(a,b)
        """
        return abs(a * b) // gcd(a, b)
        
    def simplify_fraction(self):
        try:
            input_str = self.entry1.get().strip()
            if '/' not in input_str:
                raise ValueError("请输入分数格式，如 18/24")
                
            numerator, denominator = map(int, input_str.split('/'))
            if denominator == 0:
                raise ValueError("分母不能为零")
                
            common_divisor = gcd(numerator, denominator)
            simplified_num = numerator // common_divisor
            simplified_den = denominator // common_divisor
            
            self.result_label.config(text=f"结果: {input_str} 化简为 {simplified_num}/{simplified_den}")
        except ValueError as e:
            messagebox.showerror("错误", str(e))
            
    def calculate_gcd(self):
        try:
            input_str = self.entry1.get().strip()
            if not input_str:
                raise ValueError("请输入数字")
                
            if '/' in input_str:
                raise ValueError("请使用GCD按钮计算多个数字的GCD，或用分数化简按钮化简分数")
                
            # 验证并转换输入
            numbers = []
            for num in input_str.split(','):
                num = num.strip()
                if not num:
                    continue
                if not num.lstrip('-').isdigit():
                    raise ValueError(f"'{num}' 不是有效的整数")
                numbers.append(int(num))
                
            if len(numbers) < 2:
                raise ValueError("至少需要输入两个数字，用逗号分隔")
                
            # 计算并比较两种算法的性能
            import time
            
            # 二进制算法
            start_binary = time.perf_counter()
            result_binary = gcd(*numbers, verbose=True, algorithm="binary")
            time_binary = time.perf_counter() - start_binary
            
            # 欧几里得算法
            start_euclidean = time.perf_counter()
            result_euclidean = gcd(*numbers, verbose=True, algorithm="euclidean")
            time_euclidean = time.perf_counter() - start_euclidean
            
            # 验证结果一致
            assert result_binary == result_euclidean, "两种算法结果不一致"
            
            self.result_label.config(
                text=f"结果: {', '.join(map(str, numbers))} 的最大公约数是 {result_binary}\n"
                     f"二进制算法耗时: {time_binary:.6f}秒\n"
                     f"欧几里得算法耗时: {time_euclidean:.6f}秒"
            )
        except ValueError as e:
            messagebox.showerror("输入错误", f"无效输入: {str(e)}")
            
    def calculate_lcm(self):
        try:
            num1 = int(self.entry1.get())
            num2 = int(self.entry2.get())
            gcd_result = gcd(num1, num2)
            result = self.lcm(num1, num2)
            self.result_label.config(text=f"结果: {num1}和{num2}的最小公倍数是{result}")
        except ValueError:
            messagebox.showerror("错误", "请输入有效的整数！")
            
    def start_math_game(self):
        """开始数学游戏，随机生成两个数让用户猜GCD"""
        import random
        
        # 生成1-100之间的随机数
        num1 = random.randint(1, 100)
        num2 = random.randint(1, 100)
        self.current_numbers = (num1, num2)
        self.current_gcd = gcd(num1, num2)
        
        # 更新UI
        self.result_label.config(text=f"猜一猜 {num1} 和 {num2} 的最大公约数是多少？")
        self.game_entry.grid()
        self.submit_button.grid()
        self.game_active = True
        
    def check_game_answer(self):
        """检查用户的游戏答案"""
        if not self.game_active:
            return
            
        answer_str = self.game_entry.get().strip()
        if not answer_str:
            messagebox.showerror("输入错误", "请输入答案")
            return
            
        if not answer_str.lstrip('-').isdigit():
            messagebox.showerror("输入错误", f"'{answer_str}' 不是有效的整数")
            return
            
        try:
            user_answer = int(answer_str)
            if user_answer == self.current_gcd:
                self.score += 1
                self.score_label.config(text=f"分数: {self.score}")
                messagebox.showinfo("正确", "恭喜你答对了！")
            else:
                messagebox.showinfo("错误", f"正确答案是 {self.current_gcd}")
            
            # 准备下一题
            self.start_math_game()
            self.game_entry.delete(0, tk.END)
        except ValueError:
            messagebox.showerror("输入错误", "请输入有效的整数！")

if __name__ == "__main__":
    # 运行GUI应用
    app = GCDCalculator()
    app.master.geometry("400x300+500+200")
    app.master.mainloop()
    
    # 单元测试
    import unittest
    class TestGCD(unittest.TestCase):
        def test_gcd_48_18(self):
            self.assertEqual(gcd(48, 18), 6)
        
        def test_gcd_17_5(self):
            self.assertEqual(gcd(17, 5), 1)
        
        def test_gcd_0_5(self):
            self.assertEqual(gcd(0, 5), 5)
    
    # 通过命令行参数控制是否运行测试
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == 'test':
        unittest.main(argv=['first-arg-is-ignored'])